// @ts-check

import { service as axios } from '@vimi/utils-api'
import { guid, tuid } from '@vimi/utils-tool'
import service from 'axios'

/**
 * @type {{
 * accessid?: string
 * dir?: string
 * expire?: number
 * host?: string
 * policy?: string
 * OSSAccessKeyId?: string
 * }}
 */
const stsCache = {}

// 缓存，并行请求（批量上传）时只请求认证一次
let fetching = false
const fetchQueue = []

const afterSignSuccess = () => {
  fetching = false
  fetchQueue.forEach(p => p.resolve())
  fetchQueue.length = 0
}

const afterSignError = () => {
  fetching = false
  fetchQueue.forEach(p => p.reject())
  fetchQueue.length = 0
}

/**
 * 获取 sts 认证
 * 批量上传时，缓存认证信息
 * @param {string} dir
 */
export const signature = async dir => {
  if (fetching) {
    return new Promise((resolve, reject) => {
      fetchQueue.push({ resolve, reject })
    })
  }
  fetching = true

  // 缓存认证信息 30s，设置安全间隔时间
  const EXPIRE_BUFF = 5
  if (
    stsCache.dir === dir &&
    stsCache.expire &&
    stsCache.expire > +new Date() / 1000 + EXPIRE_BUFF
  ) {
    afterSignSuccess()
    return Promise.resolve(stsCache)
  }

  // 无缓存或缓存过期，支获取 sts 认证
  try {
    const { data } = await service.post(`/auth/signature/?userDir=${dir}`)
    Object.keys(data).forEach(key => {
      stsCache[key] = data[key]
    })
    stsCache.expire = +stsCache.expire
    stsCache.OSSAccessKeyId = stsCache.accessid
    afterSignSuccess()
    return Promise.resolve(data)
  } catch (err) {
    afterSignError()
    return Promise.reject(err)
  }
}

/**
 * 生成 oss key
 * @param {string} filename
 * @param {string | 0 | 1 | 2} type
 */
const createOssKey = (filename, type) => {
  // 如果设置了文件名，则直接用该名字
  if (typeof type === 'string' || type === 0) {
    return filename
  }

  // 用guid或包括时间的随机数
  const filtType = /\.[^.]+$/.exec(filename)
  return `${type === 1 ? tuid() : guid()}${filtType}`
}

/**
 * OSS上传对象
 * @param {{
 *  file: File,
 *  dir: string,
 *  type: string | 0 | 1 | 2, // 0用文件名 1用tuid 2用guid string当作文件名
 *  progress: (e: any) => any
 * }} param
 */
export const uploadOss = async ({ file, dir, type = 1, progress = () => {} }) => {
  const useDir = dir.replace(/(^\/)|(\/$)/g, '')
  await signature(useDir)

  const action = `${stsCache.host}.aliyuncs.com`
  const { name: filename } = file
  const name = createOssKey(filename, type)
  const key = `${useDir}/${name}`

  const formData = new FormData()
  Object.keys(stsCache).forEach(k => {
    formData.append(k, stsCache[k])
  })
  formData.append('key', key)
  formData.append('file', file, filename)
  formData.append('success_action_status', '200')

  await service.post(action, formData, {
    timeout: 0,
    onUploadProgress: e => {
      if (e.total > 0) {
        e.percent = (e.loaded / e.total) * 100
      }
      progress(e)
    },
  })

  return `${action}/${key}`
}

// 删除

/**
 * 从 url 中获取 oss key
 * @param {string} url
 */
const getKeyFromUrl = url => url.replace(/^https?:\/\/[\w-.]+\/(.*)/, '$1')

/**
 * 删除 oss
 * @param {string} url
 */
const deleteOssObject = url => axios.delete(`/auth/oss/object/?objectName=${getKeyFromUrl(url)}`)

/**
 * 删除
 * @param {string | string[]} url
 */
export const deleteOss = url =>
  Array.isArray(url) ? Promise.all(url.map(item => deleteOssObject(item))) : deleteOssObject(url)
